/*******************************************************************************************
*
*   raylib [core] example - 2D Camera system
*
*   Example complexity rating: [★★☆☆] 2/4
*
*   Example originally created with raylib 1.5, last time updated with raylib 3.0
*
*   Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
*   BSD-like license that allows static linking with closed source software
*
*   Copyright (c) 2016-2025 Ramon Santamaria (@raysan5)
*

代码结构和逻辑总结：

1. 包含头文件 ：
   
   - raylib.h : Raylib 库的核心功能。
   - math.h : 用于数学运算，如 expf 和 logf 实现平滑缩放。
2. 常量定义 ：
   
   - MAX_BUILDINGS : 定义场景中建筑物的最大数量。
3. main 函数 ：
   
   - 初始化 ( Initialization ) ：
     - 设置屏幕尺寸 ( screenWidth , screenHeight )。
     - 调用 InitWindow() 创建窗口。
     - 创建玩家对象 ( player )，一个红色的矩形。
     - 创建建筑物数组 ( buildings ) 和对应的颜色数组 ( buildColors )。
     - 通过一个循环随机生成建筑物的位置、大小和颜色，并将它们放置在一个很长的水平条带上。
     - 初始化2D相机 ( camera )：
       - target : 相机试图聚焦的点，初始设置为玩家中心。
       - offset : 相机视图的偏移量，通常是屏幕中心，使得 target 显示在屏幕中央。
       - rotation : 相机的旋转角度。
       - zoom : 相机的缩放级别。
     - SetTargetFPS(60) : 将游戏设置为每秒60帧。
   - 主游戏循环 ( while (!WindowShouldClose()) ) ：这是游戏的核心，每一帧都会执行。
     - 更新逻辑 ( Update ) ：
       - 玩家移动 ：检测左右方向键，更新 player.x 坐标。
       - 相机跟随 ：将 camera.target 更新为玩家的当前中心位置。
       - 相机旋转 ：检测 'A' 和 'S' 键来旋转相机，并限制旋转范围。
       - 相机缩放 ：检测鼠标滚轮的滚动，使用 expf(logf()) 的方式来平滑地改变 camera.zoom ，并限制缩放范围。
       - 相机重置 ：检测 'R' 键，按下后将相机的 zoom 和 rotation 恢复到初始值。
     - 绘制逻辑 ( Draw ) ：
       - BeginDrawing() : 准备开始绘制。
       - ClearBackground(RAYWHITE) : 用白色清空屏幕。
       - BeginMode2D(camera) : 关键点 。此函数启用2D相机模式。在此调用之后到 EndMode2D() 之间的所有绘制命令都会根据 camera 的 target , offset , rotation , 和 zoom 进行变换。这意味着你可以在一个虚拟的“世界坐标系”中绘制物体，而相机会决定屏幕上实际看到的是哪一部分以及如何显示。
         - 绘制一个大的深灰色矩形作为背景。
         - 循环绘制所有建筑物。
         - 绘制玩家。
         - 绘制两条穿过相机目标点的绿色辅助线。
       - EndMode2D() : 结束2D相机模式。之后绘制的内容将直接绘制在屏幕坐标上，不受相机影响。
       - 绘制UI元素：如屏幕右上角的 "SCREEN AREA" 文本、屏幕边框、以及左上角的操作说明信息框和文本。这些元素因为在 EndMode2D() 之后绘制，所以它们的位置是固定的，不会随相机移动。
       - EndDrawing() : 完成当前帧的绘制，并将其显示到屏幕上。
   - 反初始化 ( De-Initialization ) ：
     - CloseWindow() : 关闭窗口并清理Raylib使用的资源。
核心概念：2D 相机 ( Camera2D )

Raylib 的 Camera2D 结构体和 BeginMode2D() / EndMode2D() 函数对是实现2D相机效果的关键。

- camera.target : 世界坐标中相机想要“看”向的点。
- camera.offset : 屏幕坐标中 target 应该被渲染到的位置。通常是屏幕中心 (screenWidth/2, screenHeight/2) ，这样 target 就会显示在屏幕的中央。
- camera.rotation : 相机围绕 offset 点的旋转角度（以度为单位）。
- camera.zoom : 缩放级别。大于1.0表示放大，小于1.0表示缩小。
当调用 BeginMode2D(camera) 时，Raylib 会设置一个变换矩阵，使得后续的 Draw... 函数调用的坐标都是相对于这个相机视图的。这使得你可以方便地创建一个比屏幕更大的游戏世界，并让相机在其中移动、旋转和缩放。

这个例子很好地展示了如何结合玩家输入来控制相机，以及如何组织游戏世界的元素和UI元素（哪些受相机影响，哪些不受）。
********************************************************************************************/

#include "raylib.h" // 引入 Raylib 核心库，包含了所有 Raylib 的功能和数据类型
#include <math.h>   // 引入数学库，用于一些数学计算，例如此处的 expf 和 logf (指数和对数函数)

#define MAX_BUILDINGS   100 // 定义一个宏，表示场景中建筑物的最大数量

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
    // Initialization
    //--------------------------------------------------------------------------------------
    const int screenWidth = 800;    // 定义屏幕宽度
    const int screenHeight = 450;   // 定义屏幕高度

    InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); // 初始化窗口和OpenGL上下文

    Rectangle player = { 400, 280, 40, 40 }; // 定义玩家的矩形，{x, y, width, height}
    Rectangle buildings[MAX_BUILDINGS] = { 0 }; // 定义一个矩形数组来存储所有建筑物的信息，初始化为0
    Color buildColors[MAX_BUILDINGS] = { 0 };   // 定义一个颜色数组来存储每个建筑物的颜色，初始化为0

    int spacing = 0; // 用于计算建筑物之间的水平间距

    // 循环初始化每个建筑物的位置、大小和颜色
    for (int i = 0; i < MAX_BUILDINGS; i++)
    {
        buildings[i].width = (float)GetRandomValue(50, 200); // 随机生成建筑物的宽度 (50到200之间)
        buildings[i].height = (float)GetRandomValue(100, 800); // 随机生成建筑物的高度 (100到800之间)
        // 计算建筑物的y坐标，使其底部对齐在屏幕下方的一个基线上 (screenHeight - 130.0f)
        buildings[i].y = screenHeight - 130.0f - buildings[i].height;
        // 计算建筑物的x坐标，从一个很靠左的位置 (-6000.0f) 开始，并加上累积的间距
        buildings[i].x = -6000.0f + spacing;

        spacing += (int)buildings[i].width; // 更新间距，为下一个建筑物腾出空间

        // 随机生成建筑物的颜色 (偏亮的颜色)
        buildColors[i] = (Color){
            (unsigned char)GetRandomValue(200, 240), // 红色分量
            (unsigned char)GetRandomValue(200, 240), // 绿色分量
            (unsigned char)GetRandomValue(200, 250), // 蓝色分量
            255};                                    // Alpha分量 (不透明)
    }

    Camera2D camera = { 0 }; // 初始化一个2D相机对象
    // 设置相机的目标点，初始时指向玩家的中心 (player.x + 20.0f, player.y + 20.0f)
    // 20.0f 是玩家宽度和高度的一半，用于定位到玩家中心
    camera.target = (Vector2){ player.x + 20.0f, player.y + 20.0f };
    // 设置相机的偏移量，通常设置为屏幕中心，这样目标点会显示在屏幕中心
    camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
    camera.rotation = 0.0f; // 设置相机的初始旋转角度为0
    camera.zoom = 1.0f;     // 设置相机的初始缩放级别为1.0 (不缩放)

    SetTargetFPS(60);                   // 设置游戏的目标帧率为每秒60帧
    //--------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())        // 主游戏循环，只要窗口没有被要求关闭 (例如用户点击关闭按钮或按下ESC键)
    {
        // Update
        //----------------------------------------------------------------------------------
        // 玩家移动控制
        if (IsKeyDown(KEY_RIGHT)) player.x += 2; // 如果按下右方向键，玩家向右移动
        else if (IsKeyDown(KEY_LEFT)) player.x -= 2; // 如果按下左方向键，玩家向左移动

        // 相机目标点跟随玩家
        // 更新相机目标点为玩家当前位置的中心
        camera.target = (Vector2){ player.x + 20, player.y + 20 };

        // 相机旋转控制
        if (IsKeyDown(KEY_A)) camera.rotation--; // 如果按下A键，相机逆时针旋转
        else if (IsKeyDown(KEY_S)) camera.rotation++; // 如果按下S键，相机顺时针旋转

        // 限制相机旋转角度在 -40 到 40 度之间
        if (camera.rotation > 40) camera.rotation = 40;
        else if (camera.rotation < -40) camera.rotation = -40;

        // 相机缩放控制
        // GetMouseWheelMove() 获取鼠标滚轮的滚动量 (向上为正，向下为负)
        // 使用对数和指数函数 (logf, expf) 来实现更平滑和一致的缩放速度
        // 每次滚动0.1f的缩放因子变化
        camera.zoom = expf(logf(camera.zoom) + ((float)GetMouseWheelMove()*0.1f));

        // 限制相机缩放级别在 0.1 (缩小10倍) 到 3.0 (放大3倍) 之间
        if (camera.zoom > 3.0f) camera.zoom = 3.0f;
        else if (camera.zoom < 0.1f) camera.zoom = 0.1f;

        // 相机重置 (缩放和旋转)
        if (IsKeyPressed(KEY_R)) // 如果按下R键 (IsKeyPressed 表示只在按下的那一帧触发)
        {
            camera.zoom = 1.0f;     // 重置缩放为1.0
            camera.rotation = 0.0f; // 重置旋转为0.0
        }
        //----------------------------------------------------------------------------------

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing(); // 开始绘制

            ClearBackground(RAYWHITE); // 用白色（自定义的RAYWHITE）清除背景

            BeginMode2D(camera); // 开始2D相机模式，之后的所有绘制操作都会受到相机变换 (平移、旋转、缩放) 的影响

                // 绘制一个巨大的深灰色矩形作为地面/背景层
                DrawRectangle(-6000, 320, 13000, 8000, DARKGRAY);

                // 循环绘制所有建筑物
                for (int i = 0; i < MAX_BUILDINGS; i++) DrawRectangleRec(buildings[i], buildColors[i]);

                // 绘制玩家 (红色矩形)
                DrawRectangleRec(player, RED);

                // 绘制两条绿色的线，分别穿过相机的目标点的x和y坐标，用于可视化相机目标
                // 这些线的长度非常大，以确保它们能横跨整个可见区域
                DrawLine((int)camera.target.x, -screenHeight*10, (int)camera.target.x, screenHeight*10, GREEN);
                DrawLine(-screenWidth*10, (int)camera.target.y, screenWidth*10, (int)camera.target.y, GREEN);

            EndMode2D(); // 结束2D相机模式，之后的绘制操作不再受相机变换影响，而是直接在屏幕坐标系上绘制

            // 在屏幕右上角绘制文本 "SCREEN AREA"
            DrawText("SCREEN AREA", 640, 10, 20, RED);

            // 绘制红色的屏幕边框
            DrawRectangle(0, 0, screenWidth, 5, RED);
            DrawRectangle(0, 5, 5, screenHeight - 10, RED);
            DrawRectangle(screenWidth - 5, 5, 5, screenHeight - 10, RED);
            DrawRectangle(0, screenHeight - 5, screenWidth, 5, RED);

            // 绘制一个半透明的天蓝色矩形作为信息提示框的背景
            DrawRectangle( 10, 10, 250, 113, Fade(SKYBLUE, 0.5f));
            // 绘制信息提示框的蓝色边框
            DrawRectangleLines( 10, 10, 250, 113, BLUE);

            // 在信息提示框内绘制控制说明文本
            DrawText("Free 2d camera controls:", 20, 20, 10, BLACK);
            DrawText("- Right/Left to move Offset", 40, 40, 10, DARKGRAY); // 这里的 "Offset" 实际上是移动玩家，从而间接移动相机目标
            DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, DARKGRAY);
            DrawText("- A / S to Rotate", 40, 80, 10, DARKGRAY);
            DrawText("- R to reset Zoom and Rotation", 40, 100, 10, DARKGRAY);

        EndDrawing(); // 结束绘制
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    CloseWindow();        // 关闭窗口并释放OpenGL上下文资源
    //--------------------------------------------------------------------------------------

    return 0; // 程序正常退出
}